/*IO = 等 + 拷贝
    select: 只负责进行等， 一次可以等待多个文件描述符。像钓鱼时可以拿多个鱼竿的人*/

/*int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout)*/
/*struct timeval
{
    time_t tv_sec;          /* 时间戳：seconds */ 
    /*suseconds_t tv_usec;    // 时间戳： 微妙mircrosseconds}*/
// 1、第一个参数：将来select等待的多个文件描述符当中文件描述符最大的fd + 1。 max(fd) +1
// 返回值：>0: 有n个fd就绪了  ==0:超时(没有错误，也没有文件描述符就绪)  <0:等待出错
// 2、最后一个参数：给select设置等待方式。struct timeval timeout = {5, 0};代表select每隔5s,timeout一次。5s以内，阻塞式等待。
// 5s期间，如果没有任何一个文件描述符就绪了，select直接返回。然后再重新进入，在设置5s的时候，重复刚刚的动作，如果在等待的5s期间，
// 有文件描述符就绪，立即返回。{0， 0}:这就是非阻塞等待。立马返回   NULL：阻塞等待。
// 也是输入输出型参数。比如我们设置的是{5,0},刚过去2s，就有文件描述符就绪了。此时timeout参数就会变成{3, 0},表示超时时间还有3s。 
// 3、fd_set是操作系统提供的数据类型，也是输入输出型参数。是位图。在很多文件描述符中，有些文件描述符我们只关心能否读取数据，有些则是关心能否写数据。
// 目前关心fd上面的事件：1、读 2、写  3、异常。如果读文件描述符就绪，可以通过该文件描述符读取数据，不会阻塞。文件描述符可都是数字啊。
// 例如readfds:  
// 输入时：用户告诉内核，我给你的一个或者多个fd，你要帮我关心fd上面的读事件哦。如果读事件就绪了，你要告诉我。
// 输出(返回)时：内核告诉用户，用户你让我关心的多个fd当中有哪些读事件就绪了，用户你赶紧读取吧。
// 例如我现在就想让操作系统关心我0,1,2,3文件描述符的读事件情况
// 传入的时候：0000 1111，比特位从右向左数的位置，表示文件描述符的编号。比特位的内容，0 or 1,是否需要内核关心。
// 传出的时候：0000 0100，表示2号文件描述符的读事件就绪了。你可以去读了。比特位的内容：0 or 1,表示哪些用户关心的fd上面的读事件已经就绪了。
// fd_set是一张位图，让用户和内核传递fd是否就绪的信息。注定了使用select的时候，一定会有大量的位图操作。但是操作系统会提供位图提供的接口。


// 优点：单进程的同时可以多用户的请求
// 缺点：1、等待的fd是有上限的。因为是用位图方式，是一种以内核编好的fd_set类型，大小是有上限的。
// 2、输入输出参数比较多，从用户到内核数据拷贝的频率比较高。
// 3、输入输出参数比较多，每次都要对关心的fd时间进行重置，导致很多次遍历。
// 4、使用自定义数组管理用户的fd, 用户层需要很多次遍历，内核当中检测fd事件就绪，也要遍历。

#include "selectServer.hpp"
#include <memory>

int main()
{
    std::unique_ptr<SelectServer> svr(new SelectServer(8080));
    svr->init();
    svr->start();
    return 0;
}